#include <iostream>

#include <gtest/gtest.h>

#include "melon/client/thrift_client_pool.h"
#include "bbt/adapters/bbt_state_adapter.h"
#include "bbt/adapters/bbt_main_logic_adapter.h"
#include "bbt/access_protocol/bbt_util.h"

using bbt::BbtMainLogicServiceClient;
using melon::client::ClientPool;

using namespace bbt;
using namespace std;

TEST(T, DistributeMessage) {
  for (int i = 0; i < 1; ++i) {
    BbtMessage msg;
    int ret = BbtMainLogicAdapter::Instance().DistributeMessage(msg, 2);
    std::cout << "DistributeMessage " << i << " ret=" << ret << std::endl;
    EXPECT_EQ(ret, 0);
  }
}

TEST(T, BusyIndex) {
  {
    EngineerStatus status;
    
    status.profile.max_slots = 2;
    status.profile.suggested_slots = 1;
    status.profile.rank = 0;
    for(size_t i = 0; i < 8; ++i) {
      status.profile.skills.push_back(i);
    }
    status.access_server_id = 1;
    status.online_type = 1;
    // status.allocated_customers();
    status.serving_customers.push_back(2);
    status.serving_customers.push_back(3);

    EXPECT_EQ(CalculateEngineerBusyIndex(status), 2000008);
  }

  {
    EngineerStatus status;
    
    status.profile.max_slots = 2;
    status.profile.suggested_slots = 1;
    status.profile.rank = 0;
    for(size_t i = 0; i < 8; ++i) {
      status.profile.skills.push_back(i);
    }
    status.access_server_id = 1;
    status.online_type = 1;
    status.allocated_customers.push_back(1);
    status.serving_customers.push_back(2);

    EXPECT_EQ(CalculateEngineerBusyIndex(status), 2000008);
  }

  {
    EngineerStatus status;
    
    status.profile.max_slots = 2;
    status.profile.suggested_slots = 2;
    status.profile.rank = 0;
    for(size_t i = 0; i < 8; ++i) {
      status.profile.skills.push_back(i);
    }
    status.access_server_id = 1;
    status.online_type = 1;
    status.allocated_customers.push_back(1);
    status.allocated_customers.push_back(5);
    status.serving_customers.push_back(2);
    status.serving_customers.push_back(3);
    status.serving_customers.push_back(4);

    EXPECT_EQ(CalculateEngineerBusyIndex(status), 2500008);
  }
}

TEST(T, AssigningLock) {
  int ret = 0;
  ret = BbtMainLogicAdapter::Instance().EngineerDisconnected(10009, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerDisconnected(10019, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerLogout(10029, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10029, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10029, 0, 2);

  int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(3330001, 0, 9, AllocType::BASIC, 2);
  EXPECT_TRUE(ret == 0);
  EXPECT_EQ(engineer, 10029);

  // 无法立即分配第二个(测试环境，间隔要求1s, 线上1分钟)
  engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(3330002, 0, 9, AllocType::BASIC, 2);
  EXPECT_TRUE(ret == 0);
  EXPECT_EQ(engineer, 0);

  usleep(1100000);

  engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(3330002, 0, 9, AllocType::BASIC, 2);
  EXPECT_TRUE(ret == 0);
  EXPECT_EQ(engineer, 10029);

  ret = BbtMainLogicAdapter::Instance().EngineerLogout(10029, 0, 2);
}

TEST(T, EngineerLogin) {
  int ret = 0;
  ret = BbtMainLogicAdapter::Instance().EngineerLogout(10028, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerLogout(10029, 0, 2);

  int64_t engineer = 0;
  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10028, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10028, 0, 2);
  engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(3330001, 0, 8, AllocType::BASIC, 2);
  EXPECT_TRUE(engineer % 10 == 8);

  ret = BbtMainLogicAdapter::Instance().EngineerLogout(10028, 0, 2);
  usleep(3000);
  engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(333000, 0, 8, AllocType::BASIC, 2);
  EXPECT_TRUE(engineer != 10028);

  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10029, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10029, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerLogout(10029, 0, 2);
  engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(3330002, 0, 9, AllocType::BASIC, 2);
  EXPECT_TRUE(ret == 0);
  EXPECT_TRUE(engineer != 10029);
}

TEST(T, AllocateEngineer) {
  int ret = 0;
  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10025, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10025, 0, 2);
  while(true) {
    for(int16_t i = 0; i < 9; ++i) {
      int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(127646892, 0, 5, AllocType::BASIC, 2);
      std::cout << "problem_type " << i << " allocated = " << engineer << std::endl;
      BbtMainLogicAdapter::Instance().DeallocateEngineer(127646892, engineer, 2);
    }
  }
  return;

  EXPECT_TRUE(ret == 0);
  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10026, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10026, 0, 2);
  EXPECT_TRUE(ret == 0);
  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10027, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10027, 0, 2);
  EXPECT_TRUE(ret == 0);
  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10028, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10028, 0, 2);
  EXPECT_TRUE(ret == 0);
  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10029, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10029, 0, 2);
  EXPECT_TRUE(ret == 0);

  for(int i = 0; i < 5; ++i) {
    int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(3330000 + i, 
        0, 5, AllocType::BASIC, 2);
    EXPECT_TRUE(engineer >= 10000 && engineer <= 10030);
    cout << "engineer=" << engineer << endl;
  }
  return;

  for(int i = 0; i < 5; ++i) {
    int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(333000, 0, 5, AllocType::BASIC, 2);
    EXPECT_EQ(engineer, 0);
  }
  return;
  usleep(1100000);

  for(int i = 0; i < 5; ++i) {
    int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(3330000 + 10 + i,
        0, 1, AllocType::BASIC, 2);
    EXPECT_TRUE(engineer >= 10000 && engineer <= 10005);
  }

  usleep(1100000);

  for(int i = 0; i < 5; ++i) {
    int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(333000 + (20 + i), 0, 1, AllocType::BASIC, 2);
    EXPECT_TRUE(engineer >= 10000 && engineer <= 10005);
  }

  usleep(1100000);

  for(int i = 0; i < 5; ++i) {
    int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(333000 + (30 + i), 0, 1, AllocType::BASIC, 2);
    EXPECT_TRUE(engineer >= 10000 && engineer <= 10005);
  }

  usleep(1100000);

  for(int i = 0; i < 5; ++i) {
    int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(333000 + (40 + i), 0, 1, AllocType::BASIC, 2);
    std::cout << "---------- " << engineer << std::endl;
    EXPECT_TRUE(engineer >= 10000 && engineer <= 10005);
  }

  usleep(1100000);

  for(int i = 0; i < 5; ++i) {
    int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(3330000 + 40 + i, 0, 1, AllocType::BASIC, 2);
    EXPECT_EQ(engineer, 0);
  }
  
  // 避免影响别人
//ret = BbtMainLogicAdapter::Instance().EngineerLogout(10001, 0, 2);
//ret = BbtMainLogicAdapter::Instance().EngineerLogout(10002, 0, 2);
//ret = BbtMainLogicAdapter::Instance().EngineerLogout(10003, 0, 2);
//ret = BbtMainLogicAdapter::Instance().EngineerLogout(10004, 0, 2);
//ret = BbtMainLogicAdapter::Instance().EngineerLogout(10005, 0, 2);
}


TEST(T, Assignment) {
  int ret = BbtMainLogicAdapter::Instance().EngineerLogout(10029, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10029, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10029, 0, 2);

  int64_t customer(33300021);
  int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(customer, 
        0, 9, AllocType::BASIC, 2);
  EXPECT_EQ(ret, 0);
  EXPECT_EQ(engineer, 10029);

  Assignment assignment;
  ret = BbtMainLogicAdapter::Instance().StartAssignment(customer, engineer,
        9, AllocType::BASIC, "18611951882", &assignment, 3);
  std::cout << "assignment.id=" << assignment.id << std::endl;
  EXPECT_TRUE(assignment.id > 0);

  EngineerStatus status;
  ret = BbtStateAdapter::Instance().GetEngineerStatus(engineer, &status, 2);

  EXPECT_EQ(status.allocated_customers.size(), 0);
  EXPECT_EQ(status.serving_customers.size(), 1);
  EXPECT_EQ(status.serving_customers[0], customer);
  CustomerStatus customer_status;
  ret = BbtStateAdapter::Instance().GetCustomerStatus(customer, &customer_status, 2);
  std::cout << "GetCustomerStatus " << customer << " ret=" << ret << std::endl;
  EXPECT_EQ(customer_status.assignment_id, assignment.id);
  // EXPECT_EQ(customer_status.assignment_state, AssignmentState::SERVING);
  EXPECT_EQ(customer_status.serving_engineer, engineer);

  sleep(2);
  int64_t customer2(33300022);
  engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(customer2, 
        0, 9, AllocType::BASIC, 2);

  std::cout << "allcated engineer=" << engineer << std::endl;
  EngineerStatus status2;
  ret = BbtStateAdapter::Instance().GetEngineerStatus(engineer, &status2, 2);
  EXPECT_EQ(status2.allocated_customers.size(), 1);
  EXPECT_EQ(status2.serving_customers.size(), 1);
  EXPECT_EQ(status2.allocated_customers[0], customer2);

  ret = BbtMainLogicAdapter::Instance().CompleteAssignment(assignment.id, 
        customer, engineer, AssignmentState::SOLVED, 2);
  EngineerStatus status3;
  ret = BbtStateAdapter::Instance().GetEngineerStatus(engineer, &status3, 2);
  EXPECT_EQ(status3.allocated_customers.size(), 1);
  EXPECT_EQ(status3.serving_customers.size(), 0);
}

TEST(T, TransferAssignment) {
  int ret = BbtMainLogicAdapter::Instance().EngineerLogout(10029, 0, 2);

  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10029, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10029, 0, 2);

  int64_t customer = 33300022;
  int64_t engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(customer, 
        0, 8, AllocType::BASIC, 2);
  EXPECT_EQ(ret, 0);
  EXPECT_EQ(engineer, 10029);

  Assignment assignment;
  ret = BbtMainLogicAdapter::Instance().StartAssignment(customer, engineer,
        8, AllocType::BASIC, "18611951882", &assignment, 3);
  std::cout << "assignment.id=" << assignment.id << std::endl;
  EXPECT_TRUE(assignment.id > 0);
  EngineerStatus status4;
  ret = BbtStateAdapter::Instance().GetEngineerStatus(engineer, &status4, 2);
  EXPECT_EQ(status4.allocated_customers.size(), 0);
  EXPECT_EQ(status4.serving_customers.size(), 1);

  ret = BbtMainLogicAdapter::Instance().EngineerLogout(10028, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerLogin(10028, 0, 2);
  ret = BbtMainLogicAdapter::Instance().EngineerActivated(10028, 0, 2);

  int64_t new_engineer = BbtMainLogicAdapter::Instance().AllocateEngineer(customer, 0,
        8, (AllocType::type)(AllocType::BASIC | T_TRANSFER_BIT), 2);
  EXPECT_EQ(new_engineer, 10028);

  EngineerStatus status3;
  ret = BbtStateAdapter::Instance().GetEngineerStatus(new_engineer, &status3, 2);
  EXPECT_EQ(status3.allocated_customers.size(), 1);
  EXPECT_EQ(status3.serving_customers.size(), 0);

  BbtMainLogicAdapter::Instance().TransferAssignment(assignment.id, customer,
        engineer, new_engineer, AllocType::BASIC, 8, 3);

  EngineerStatus status;
  ret = BbtStateAdapter::Instance().GetEngineerStatus(engineer, &status, 2);
  EXPECT_EQ(status.allocated_customers.size(), 0);
  EXPECT_EQ(status.serving_customers.size(), 0);

  EngineerStatus status2;
  ret = BbtStateAdapter::Instance().GetEngineerStatus(new_engineer, &status2, 2);
  EXPECT_EQ(status2.allocated_customers.size(), 0);
  EXPECT_EQ(status2.serving_customers.size(), 1);
  EXPECT_EQ(status2.serving_customers[0], customer);
}

struct Args {
  Args(int c, char ** v) : argc(c), argv(v) {}
  int argc;
  char** argv;
};

void * ThreadFun(void * p) {
  Args * args = (Args*)p;
  testing::InitGoogleTest(&args->argc, args->argv);
  return (void*)RUN_ALL_TESTS();
}

const int kThreadCount = 1;

int main(int argc, char ** argv) {
  LOG_INIT("./test.log", "DEBUG");
  pthread_t ts[kThreadCount];
  Args args(argc, argv); 
  for (int i = 0; i < kThreadCount; ++i) {
    pthread_create(&ts[i], NULL, ThreadFun, &args);
  }

  for (int i = 0; i < kThreadCount; ++i) {
    pthread_join(ts[i], NULL);
  }
  return 0;
} 

